package com.mapbox.navigation.examples.core

import android.location.Location
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.geojson.Point
import com.mapbox.maps.CameraOptions
import com.mapbox.maps.MapboxMap
import com.mapbox.maps.Style
import com.mapbox.navigation.examples.core.databinding.LayoutActivityMultipleManeuverArrowBinding
import com.mapbox.navigation.ui.maps.NavigationStyles
import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider
import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowApi
import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowView
import com.mapbox.navigation.ui.maps.route.arrow.model.ManeuverArrow
import com.mapbox.navigation.ui.maps.route.arrow.model.RouteArrowOptions

/**
 * This activity demonstrates using the [MapboxRouteArrowApi] to add multiple arrows to a map.
 * This example will display the map at a specific location and when the 'Add Arrow' or
 * 'Remove Arrow' buttons are pressed will display predefined arrows.
 *
 * In your application you may add multiple arrows using any collection of 2 or more points.
 *
 */
class MapboxMultipleArrowActivity : AppCompatActivity() {

    private val binding: LayoutActivityMultipleManeuverArrowBinding by lazy {
        LayoutActivityMultipleManeuverArrowBinding.inflate(layoutInflater)
    }

    private val mapboxMap: MapboxMap by lazy {
        binding.mapView.getMapboxMap()
    }

    // One of the more important options is the 'aboveLayerId' option which determines the
    // elevation of layer hosting the arrows. The arrow layer will be placed above the layer ID
    // specified. By default the RouteLayerConstants.TOP_LEVEL_ROUTE_LINE_LAYER_ID is used
    // which is the layer hosting the line representing a route's traffic.
    // This default depends on the MapboxRouteLineView being initialized first. There is no
    // route line being used in this example so a layer ID is being specified.
    private val routeArrowOptions by lazy {
        RouteArrowOptions.Builder(this).withAboveLayerId("road-label").build()
    }

    // The MapboxRouteArrowApi is responsible for generating data describing the layer state
    // for the Map. The data generated should be rendered with the MapboxRouteArrowView.
    private val mapboxArrowApi: MapboxRouteArrowApi by lazy {
        MapboxRouteArrowApi()
    }

    // The MapboxRouteArrowView is responsible for rendering the data generated by
    // MapboxRouteArrowApi. Calls to MapboxRouteArrowApi will not modify the map. Map mutations
    // will not be visualized until the data is rendered by MapboxRouteArrowView.
    private val routeArrowView: MapboxRouteArrowView by lazy {
        MapboxRouteArrowView(routeArrowOptions)
    }

    private val navigationLocationProvider by lazy {
        NavigationLocationProvider()
    }

    // An arrow used for this example. The arrow head is placed on the last point in the
    // collection. The bearing of the arrow head is calculated based on the second to last
    // point to the last point.
    private val arrow0 by lazy {
        ManeuverArrow(
            listOf(
                Point.fromLngLat(-122.432034, 37.775755),
                Point.fromLngLat(-122.431229, 37.775865),
                Point.fromLngLat(-122.431293, 37.776255)
            )
        )
    }

    // An arrow used for this example.
    private val arrow1 by lazy {
        ManeuverArrow(
            listOf(
                Point.fromLngLat(-122.431481, 37.777082),
                Point.fromLngLat(-122.431610, 37.777722),
                Point.fromLngLat(-122.431969, 37.777663)
            )
        )
    }

    // An arrow used for this example.
    private val arrow2 by lazy {
        ManeuverArrow(
            listOf(
                Point.fromLngLat(-122.432886, 37.777557),
                Point.fromLngLat(-122.433251, 37.777515),
                Point.fromLngLat(-122.433165, 37.777095)
            )
        )
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        init()
        Toast.makeText(
            this,
            "Click the add arrow button to add your first arrow.",
            Toast.LENGTH_LONG
        ).show()
    }

    private fun init() {
        initStyle()
        initLocation()
    }

    private fun initStyle() {
        mapboxMap.loadStyleUri(
            NavigationStyles.NAVIGATION_DAY_STYLE,
            object : Style.OnStyleLoaded {
                override fun onStyleLoaded(style: Style) {
                    initListeners(style)
                }
            }
        )
    }

    // Using a hardcoded location for brevity
    private fun initLocation() {
        val location = Location("").also {
            it.latitude = 37.77631058427689
            it.longitude = -122.43275730442387
        }

        val point = Point.fromLngLat(-122.43275730442387, 37.77631058427689)
        val cameraOptions = CameraOptions.Builder().center(point).zoom(16.0).build()
        mapboxMap.setCamera(cameraOptions)
        navigationLocationProvider.changePosition(
            location,
            listOf(),
            null,
            null
        )
    }

    private fun initListeners(style: Style) {
        binding.btnAdd.setOnClickListener {
            when (mapboxArrowApi.getArrows().size) {
                0 -> {
                    // Notice the result of the call to addArrow is rendered by the routeArrowView.
                    mapboxArrowApi.addArrow(arrow0).apply {
                        routeArrowView.render(style, this)
                    }
                }
                1 -> {
                    mapboxArrowApi.addArrow(arrow1).apply {
                        routeArrowView.render(style, this)
                    }
                }
                2 -> {
                    mapboxArrowApi.addArrow(arrow2).apply {
                        routeArrowView.render(style, this)
                    }
                }
            }

            handleButtonVisibility()
        }

        binding.btnRemove.setOnClickListener {
            when (mapboxArrowApi.getArrows().size) {
                0 -> {
                }
                1 -> {
                    mapboxArrowApi.removeArrow(arrow0).apply {
                        routeArrowView.render(mapboxMap.getStyle()!!, this)
                    }
                    binding.btnRemove.visibility = View.INVISIBLE
                }
                2 -> {
                    mapboxArrowApi.removeArrow(arrow1).apply {
                        routeArrowView.render(mapboxMap.getStyle()!!, this)
                    }
                }
                3 -> {
                    mapboxArrowApi.removeArrow(arrow2).apply {
                        routeArrowView.render(mapboxMap.getStyle()!!, this)
                    }
                }
            }

            handleButtonVisibility()
        }
    }

    private fun handleButtonVisibility() {
        if (mapboxArrowApi.getArrows().size in 0..2) {
            binding.btnAdd.visibility = View.VISIBLE
        } else {
            binding.btnAdd.visibility = View.INVISIBLE
        }

        if (mapboxArrowApi.getArrows().isNotEmpty()) {
            binding.btnRemove.visibility = View.VISIBLE
        } else {
            binding.btnRemove.visibility = View.INVISIBLE
        }
    }
}
